/**
 * @name Deserialization of user-controlled data (Apache Struts)
 * @description Deserializing user-controlled data may allow attackers to
 *              execute arbitrary code.
 * @kind path-problem
 * @problem.severity error
 * @precision high
 * @id java/unsafe-deserialization
 * @tags security
 *       external/cwe/cwe-502
 */
import java
import UnsafeDeserialization
import semmle.code.java.dataflow.DataFlow3::DataFlow3 as DF
import DF::PathGraph

/** The interface `org.apache.struts2.rest.handler.ContentTypeHandler`. */
class ContentTypeHandler extends RefType {
  ContentTypeHandler() {
    this.hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")
  }
}

/** A `toObject` method on a subtype of `org.apache.struts2.rest.handler.ContentTypeHandler`. */
class ContentTypeHandlerDeserialization extends Method {
  ContentTypeHandlerDeserialization() {
    this.getDeclaringType().getASupertype*() instanceof ContentTypeHandler and
    this.hasName("toObject")
  }
}

/**
 * The first parameter of a `toObject` method on a `ContentTypeHandler`, which is
 * a source of tainted user data.
 */
class ContentTypeHandlerInput extends DF::Node {
  ContentTypeHandlerInput() {
    this.asParameter() = any(ContentTypeHandlerDeserialization des).getParameter(0)
  }
}

/**
 * Configuration that tracks the flow of taint from the first parameter of
 * `ContentTypeHandler.toObject` to an instance of unsafe deserialization.
 */
class StrutsUnsafeDeserializationConfig extends DF::Configuration {
  StrutsUnsafeDeserializationConfig() { this = "StrutsUnsafeDeserializationConfig" }
  override predicate isSource(DF::Node source) { source instanceof ContentTypeHandlerInput }
  override predicate isSink(DF::Node sink) { sink instanceof UnsafeDeserializationSink }
}

from DF::PathNode source, DF::PathNode sink, StrutsUnsafeDeserializationConfig conf
where conf.hasFlowPath(source, sink)
  and sink.getNode() instanceof UnsafeDeserializationSink
select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink, "Unsafe deserialization of $@.", source, "user input"
